<?php
class SecureStorage implements ArrayAccess {
    private $aData = array ();
    private $pathToStorageFiles;
    private $secret;
    private $hFile = null;
    
    public function __construct($secret, $pathToStorageFiles) {
        $this->secret = $secret;
        $this->pathToStorageFiles = $pathToStorageFiles;
        
        register_shutdown_function(array ($this, "_saveToFile"));
        
        $this->loadFromFile();
    }
    
    private function loadFromFile() {
        if ($this->hFile !== null) {
            $this->_saveToFile();
        }
        
        $filePath = $this->filePath();
        
        $this->hFile = @fopen($filePath, "a+");
        if ($this->hFile === false) {
            throw new SecureStorageException("Can't open file $filePath");
        }
        
        fseek($this->hFile, 0);
        
        if (!@flock($this->hFile, LOCK_EX)) {
            @fclose($this->hFile);
            $this->hFile = null;
            throw new SecureStorageException("Can't obtain lock for file $filePath");
        }
        
        clearstatcache();
        $fileSize = filesize($filePath);
        
        if ($fileSize > 0) {
            $encryptedData = fread($this->hFile, $fileSize);
            
            $jsonData = @mcrypt_ecb(MCRYPT_3DES, $this->secret, $encryptedData, MCRYPT_DECRYPT);
            $jsonData = preg_replace("`\\0+$`", "", $jsonData);
            //$this->log("JSON: $jsonData");
            
            $result = json_decode($jsonData, true);
            if ($result === null) {
                throw new SecureStorageException("Storage is corrupted");
            }
            
            $this->aData = $result;
        }
        else {
            $this->aData = array ();
        }
    }
    
    private function log($message) {
        error_log('[' . date("H:i:s") . '] ' . $message . "\n", 3, __FILE__ . ".log");
    }
    
    public function _saveToFile() {
        if ($this->hFile !== null) {
            $jsonData = json_encode($this->aData);
            
            $encrypted = @mcrypt_ecb(MCRYPT_3DES, $this->secret, $jsonData, MCRYPT_ENCRYPT);
            
            fseek($this->hFile, 0);
            ftruncate($this->hFile, 0);
            fwrite($this->hFile, $encrypted);
            
            if (!@flock($this->hFile, LOCK_UN)) {
                @fclose($this->hFile);
                $this->hFile = null;
                throw new SecureStorageException("Can't release lock for file $filePath");
            }
            
            if (!@fclose($this->hFile)) {
                $this->hFile = null;
                throw new SecureStorageException("Can't close file $filePath");
            }
            
            $this->hFile = null;
        }
    }
    
    private function filePath() {
        $fileName = sha1($this->secret);
        $filePath = $this->pathToStorageFiles . $fileName;
        return $filePath;
    }
    
    public function get($name) {
        if (!$this->offsetExists($name)) {
            throw new SecureStorageException("Data for key $name does not exists");
        }
        
        return $this->aData[$name];
    }
    
    public function set($name, $value) {
        $this->aData[$name] = $value;
    }
    
    public function offsetExists($offset) {
        return array_key_exists($offset, $this->aData);
    }
    
    public function offsetGet($offset) {
        return $this->get($offset);
    }
    
    public function offsetSet($offset, $value) {
        $this->set($offset, $value);
    }
    
    public function offsetUnset($offset) {
        if ($this->offsetExists($offset)) {
            unset($this->aData[$offset]);
        }
    }
    
    public function getAll() {
        return $this->aData;
    }
}

class SecureStorageException extends Exception {}
